# Copyright (c) 2019 Presto Labs Pte. Ltd.
# Author: jaewon

import logging

import experimental.prophet as prophet
from experimental.prophet.craps.universe import universe_all


def single_ref_theo_price(symbol_info, basis_window, ref_symbol_info, debug=False):
  feed = prophet.fastfeed_coin(symbol_info.product, symbol_info.sub_req)
  ref_feed = prophet.fastfeed_coin(ref_symbol_info.product, ref_symbol_info.sub_req)

  book = prophet.book(feed, inverse_product=symbol_info.inverse)
  ask0_p = book.estimated_ask0_price.named_ref('b.ask0_p')
  bid0_p = book.estimated_bid0_price.named_ref('b.bid0_p')

  price_multiplier = ref_symbol_info.price_multiplier / symbol_info.price_multiplier
  ref_book = prophet.book(ref_feed, inverse_product=ref_symbol_info.inverse)
  ref_ask0_p = (ref_book.estimated_ask0_price * price_multiplier).named_ref('ref.b.ask0_p')
  ref_bid0_p = (ref_book.estimated_bid0_price * price_multiplier).named_ref('ref.b.bid0_p')

  ask_basis = ask0_p - ref_ask0_p
  bid_basis = bid0_p - ref_bid0_p
  ask_basis_ma = prophet.time_moving_window(ask_basis,
                                            basis_window).mean().named_ref('ask_basis_ma')
  bid_basis_ma = prophet.time_moving_window(bid_basis,
                                            basis_window).mean().named_ref('bid_basis_ma')

  theo_ask0_p = (ref_ask0_p + ask_basis_ma).named_ref('theo_ask0_p')
  theo_bid0_p = (ref_bid0_p + bid_basis_ma).named_ref('theo_bid0_p')

  return theo_ask0_p, theo_bid0_p


def theo_price(symbol_info, date, **kwargs):
  target_coin = symbol_info.product.base.currency.lower()
  assert target_coin in ('btc', 'eth')

  if target_coin == 'btc':
    return btc_theo_price(symbol_info, date, **kwargs)
  elif target_coin == 'eth':
    return eth_theo_price(symbol_info, date, **kwargs)
  else:
    raise ValueError(target_coin)


# -------------------------------------
# BTC
# -------------------------------------
def btc_theo_price_1(symbol_info, date):
  universe = {s.nickname: s for s in universe_all(date)}

  theo_ask0_p0, theo_bid0_p0 = single_ref_theo_price(symbol_info,
                                                     '20m',
                                                     ref_symbol_info=universe['bitmex-xbtusd'])
  theo_ask0_p1, theo_bid0_p1 = single_ref_theo_price(
      symbol_info, '10m', ref_symbol_info=universe['okex-btcusd-this_week'])
  theo_ask0_p2, theo_bid0_p2 = single_ref_theo_price(
      symbol_info, '10m', ref_symbol_info=universe['okex-btcusd-quarter'])

  theo_ask0_p = ((theo_ask0_p0 + theo_ask0_p1 + theo_ask0_p2) / 3.).named_ref('theo_ask0_p')
  theo_bid0_p = ((theo_bid0_p0 + theo_bid0_p1 + theo_bid0_p2) / 3.).named_ref('theo_bid0_p')
  return theo_ask0_p, theo_bid0_p


def btc_theo_price_2(symbol_info, date):
  universe = {s.nickname: s for s in universe_all(date)}
  theo_ask0_p0, theo_bid0_p0 = single_ref_theo_price(symbol_info,
                                                     '20m',
                                                     ref_symbol_info=universe['bitmex-xbtusd'])
  theo_ask0_p1, theo_bid0_p1 = single_ref_theo_price(
      symbol_info, '10m', ref_symbol_info=universe['okex-btcusd-quarter'])
  theo_ask0_p2, theo_bid0_p2 = single_ref_theo_price(
      symbol_info, '10m', ref_symbol_info=universe['okex-btcusd-next_week'])

  theo_ask0_p = ((theo_ask0_p0 + theo_ask0_p1 + theo_ask0_p2) / 3.).named_ref('theo_ask0_p')
  theo_bid0_p = ((theo_bid0_p0 + theo_bid0_p1 + theo_bid0_p2) / 3.).named_ref('theo_bid0_p')
  return theo_ask0_p, theo_bid0_p


def btc_theo_price_3(symbol_info, date):
  universe = {s.nickname: s for s in universe_all(date)}

  theo_ask0_p0, theo_bid0_p0 = single_ref_theo_price(symbol_info,
                                                     '20m',
                                                     ref_symbol_info=universe['bitmex-xbtusd'])
  theo_ask0_p1, theo_bid0_p1 = single_ref_theo_price(
      symbol_info, '10m', ref_symbol_info=universe['okex-btcusd-this_week'])
  theo_ask0_p2, theo_bid0_p2 = single_ref_theo_price(
      symbol_info, '20s', ref_symbol_info=universe['okex-btcusd-quarter'])

  theo_ask0_p = ((theo_ask0_p0 + theo_ask0_p1 + theo_ask0_p2) / 3.).named_ref('theo_ask0_p')
  theo_bid0_p = ((theo_bid0_p0 + theo_bid0_p1 + theo_bid0_p2) / 3.).named_ref('theo_bid0_p')
  return theo_ask0_p, theo_bid0_p


def btc_theo_price(symbol_info, date, *, version=1):
  logging.info('lt_model_version = %s', str(version))
  if version == 1:
    return btc_theo_price_1(symbol_info, date)
  elif version == 2:
    return btc_theo_price_2(symbol_info, date)
  elif version == 3:
    return btc_theo_price_3(symbol_info, date)
  else:
    raise ValueError('Invalid version')


# -------------------------------------
# ETH
# -------------------------------------
def eth_theo_price_1(symbol_info, date):
  universe = {s.nickname: s for s in universe_all(date)}

  theo_ask0_p0, theo_bid0_p0 = single_ref_theo_price(
      symbol_info, '10m', ref_symbol_info=universe['huobi-ethusd-quarter'])
  theo_ask0_p1, theo_bid0_p1 = single_ref_theo_price(
      symbol_info, '10m', ref_symbol_info=universe['okex-ethusd-this_week'])
  theo_ask0_p2, theo_bid0_p2 = single_ref_theo_price(
      symbol_info, '10m', ref_symbol_info=universe['okex-ethusd-quarter'])

  theo_ask0_p = ((theo_ask0_p0 + theo_ask0_p1 + theo_ask0_p2) / 3.).named_ref('theo_ask0_p')
  theo_bid0_p = ((theo_bid0_p0 + theo_bid0_p1 + theo_bid0_p2) / 3.).named_ref('theo_bid0_p')
  return theo_ask0_p, theo_bid0_p


def eth_theo_price_2(symbol_info, date):
  universe = {s.nickname: s for s in universe_all(date)}
  theo_ask0_p0, theo_bid0_p0 = single_ref_theo_price(symbol_info,
                                                     '20m',
                                                     ref_symbol_info=universe['binance-ethusdt'])
  theo_ask0_p1, theo_bid0_p1 = single_ref_theo_price(
      symbol_info, '10m', ref_symbol_info=universe['okex-ethusd-quarter'])
  theo_ask0_p2, theo_bid0_p2 = single_ref_theo_price(
      symbol_info, '10m', ref_symbol_info=universe['okex-ethusd-this_week'])

  theo_ask0_p = ((theo_ask0_p0 + theo_ask0_p1 + theo_ask0_p2) / 3.).named_ref('theo_ask0_p')
  theo_bid0_p = ((theo_bid0_p0 + theo_bid0_p1 + theo_bid0_p2) / 3.).named_ref('theo_bid0_p')
  return theo_ask0_p, theo_bid0_p


def eth_theo_price_3(symbol_info, date):
  universe = {s.nickname: s for s in universe_all(date)}

  theo_ask0_p0, theo_bid0_p0 = single_ref_theo_price(
      symbol_info, '10m', ref_symbol_info=universe['huobi-ethusd-quarter'])
  theo_ask0_p1, theo_bid0_p1 = single_ref_theo_price(symbol_info,
                                                     '10m',
                                                     ref_symbol_info=universe['bitmex-ethusd'])
  theo_ask0_p2, theo_bid0_p2 = single_ref_theo_price(
      symbol_info, '10m', ref_symbol_info=universe['okex-ethusd-quarter'])

  theo_ask0_p = ((theo_ask0_p0 + theo_ask0_p1 + theo_ask0_p2) / 3.).named_ref('theo_ask0_p')
  theo_bid0_p = ((theo_bid0_p0 + theo_bid0_p1 + theo_bid0_p2) / 3.).named_ref('theo_bid0_p')
  return theo_ask0_p, theo_bid0_p


def eth_theo_price(symbol_info, date, *, version=1):
  logging.info('lt_model_version = %s', str(version))
  if version == 1:
    return eth_theo_price_1(symbol_info, date)
  elif version == 2:
    return eth_theo_price_2(symbol_info, date)
  elif version == 3:
    return eth_theo_price_3(symbol_info, date)
  else:
    raise ValueError('Invalid version')
